Django documentation

Outputting PDFs with Django

This document explains how to output PDF files dynamically using Django views. This is made possible by the excellent, open-source ReportLab Python PDF library.

The advantage of generating PDF files dynamically is that you can create customized PDFs for different purposes -- say, for different users or different pieces of content.

For example, Django was used at kusports.com to generate customized, printer-friendly NCAA tournament brackets, as PDF files, for people participating in a March Madness contest.

Install ReportLab

Download and install the ReportLab library from http://www.reportlab.org/downloads.html. The user guide (not coincidentally, a PDF file) explains how to install it.

Test your installation by importing it in the Python interactive interpreter:

>>> import reportlab

If that command doesn't raise any errors, the installation worked.

Write your view

The key to generating PDFs dynamically with Django is that the ReportLab API acts on file-like objects, and Django's HttpResponse objects are file-like objects.

Note

For more information on HttpResponse objects, see Request and response objects.

Here's a "Hello World" example:

from reportlab.pdfgen import canvas
from django.utils.httpwrappers import HttpResponse

def some_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'

    # Create the PDF object, using the response object as its "file."
    p = canvas.Canvas(response)

    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    p.drawString(100, 100, "Hello world.")

    # Close the PDF object cleanly, and we're done.
    p.showPage()
    p.save()
    return response

The code and comments should be self-explanatory, but a few things deserve a mention:

  • The response gets a special mimetype, application/pdf. This tells browsers that the document is a PDF file, rather than an HTML file. If you leave this off, browsers will probably interpret the output as HTML, which would result in ugly, scary gobbledygook in the browser window.
  • The response gets an additional Content-Disposition header, which contains the name of the PDF file. This filename is arbitrary: Call it whatever you want. It'll be used by browsers in the "Save as..." dialogue, etc.
  • Hooking into the ReportLab API is easy: Just pass response as the first argument to canvas.Canvas. The Canvas class expects a file-like object, and HttpResponse objects fit the bill.
  • Note that all subsequent PDF-generation methods are called on the PDF object (in this case, p) -- not on response.
  • Finally, it's important to call showPage() and save() on the PDF file.

Comments

paolo September 30, 2005 at 5:39 p.m.

Another very impressive application possible thanks to django's flexibility is to generate via view and serve, via 'application/vnd.oasis.opendocument.text' mimetype, xml documents that will be automatically rendered for you by OpenOffice suite. In this way you use django as an interface to the data, demanding the presentation of them to an external (specialized) application.

Dustin September 30, 2005 at 5:52 p.m.

I've recently done exactly this in one of my projects. It's good to see that my guess at how to do it was right. :)

However, I'm a bit concerned about the efficiency HttpResponse's file behavior. This seems like it would be very inefficient when dealing with large PDFs and it would perhaps be better to get either greater control of the response, or to be able to hint to HttpResponse that the response is going to be large and it should perhaps use a temporary file to buffer it.

Adrian Holovaty September 30, 2005 at 6:36 p.m.

paolo: Excellent idea. It'd be great if we had a separate document for that. Would you be willing to write it up and post it to a ticket?

Dustin: Feel free to bring this up on the django-developers mailing list, or if you have any specific suggestions please post a ticket. I haven't encountered any performance problems with this method personally, but it'd be great to quash any potential problems, if there is that potential.

paolo October 3, 2005 at 7:52 a.m.

Done. [#585]

A.C. October 4, 2005 at 11:50 a.m.

Nice. It would be great if Django could have render-to-PDF in it's admin interfaces to allow printing of simple reports of the content (grid views, form views).

David S. October 10, 2005 at 1:52 p.m.

When you set response['Content-Disposition'], drop the 'attachment;' to allow default handling of the content type. With 'attachment;' you force the dialog box prompting/confirming how to handle the document even if you have a default set on your machine.

David S. October 10, 2005 at 2:12 p.m.

A tip: If you are creating a more complex document with ReportLab you will probably want to use
cStringIO.StringIO as in:

from cStringIO import StringIO
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'filename=somefile.pdf'
...#do stuff like build story
buffer = StringIO()
doc = SimpleDocTemplate(buffer, pagesize=landscape(letter))
doc.build(story)
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response

Post a comment

Note: Please only use the comments for questions/critcisms/suggestions on the docs; if you experience errors please file a ticket, ask in the IRC channel, or post to the django-users list. Comments will be periodically reviewed, integrated into the documentation proper, and removed.

Your name:

Comment: